package oauth2

import (
    "errors"
    "time"
    "github.com/astaxie/beego/orm"
    "github.com/astaxie/beego/cache"
    "github.com/udoyu/utils/uuid"
    "code.google.com/p/goprotobuf/proto"
    ."git.oschina.net/xyit/oauth2s/db"
)

const (
    ONE_WEEK = 3600 * 24 * 7
    TWO_WEEK = ONE_WEEK * 2
)

var globalCache cache.Cache

func InitCache(c cache.Cache) {
    globalCache = c
}

func init() {
    orm.RegisterModel(new(ApplyStore), 
                      new(TokenStore))
//    bm, err := cache.NewCache("memory", `{"interval":60}`)
//    if err != nil {
//        beego.Error("cache init error|err=", err.Error())
//    }
//    globalCache = bm
}

func WebApply(req *WebApplyReq, resp *WebApplyResp) error {
    appkey := uuid.NewUuid()
    secret := uuid.NewUuid()
    was := ApplyStore{
                Creator:req.Uid,
                Name   :req.Name,
                Url    :req.Url,
                Appkey :proto.String(appkey),
                Secret :proto.String(secret),
                Status :proto.Int32(int32(1)),
            }
    o := orm.NewOrm()
    _, err := o.Insert(&was)
    if err != nil {
        return err
    }
    resp.Appkey = proto.String(appkey)
    resp.Secret = proto.String(secret)
    return nil
}

func AuthCode(req *AuthCodeReq, resp *AuthCodeResp) error{
    //存缓存
    code := uuid.NewUuid()
    code_key := code + "_" + *req.ClientId
    globalCache.Put(code_key, *req.Uid, 600)
    resp.Code = proto.String(code)
    resp.State = req.State
    scope_key := code_key + "_scope"
    globalCache.Put(scope_key, *req.Scope, 600)
    return nil
}

func AccessToken(req *AccessTokenReq, resp *AccessTokenResp) error{
    var err error
    //校验code
    code_key := *req.Code + "_" + *req.ClientId
    scope_key := code_key + "_scope"
    v_auth_uid := globalCache.Get(code_key)
    v_scope := globalCache.Get(scope_key)
    if v_auth_uid == nil {
        return errors.New("code is not right!")
    }
    globalCache.Delete(code_key)
    globalCache.Delete(scope_key)
    auth_uid := v_auth_uid.(uint32)
    scope := "base"
    if v_scope != nil {
        scope = v_scope.(string)
    }
    //校验appkey和secret
    o := orm.NewOrm()
    applystore := new(ApplyStore)
    err = o.QueryTable("apply_store").Filter("appkey", *req.ClientId).Filter("secret", *req.ClientSecret).One(applystore)
    if err != nil {
        return nil
    }

    //1-查询token
    now := time.Now().Unix()
    token := TokenStore{}
    err = o.QueryTable("token_store").Filter("uid", auth_uid).Filter("client_id", *req.ClientId).One(&token)

    //2-如果数据库里没有则插入新记录
    if err == orm.ErrNoRows {
        token.ClientId = req.ClientId
        token.Uid = proto.Uint32(auth_uid)
        token.Scope = proto.String(scope)
        token.AccessToken = proto.String(uuid.NewUuid())
        token.ExpireIn = proto.Int64(now + TWO_WEEK)
        _, err = o.Insert(&token)
        if err != nil {
            return err
        }
    } else if err != nil {
        return err
    }

    //3-如果过期则更新token
    if *token.ExpireIn < now {
        token.ExpireIn = proto.Int64(now + TWO_WEEK)
        token.AccessToken = proto.String(uuid.NewUuid())
    }
    expire := *token.ExpireIn - now
    globalCache.Put(*token.AccessToken, auth_uid, expire)
    if v_scope != nil {
        globalCache.Put(*token.AccessToken + "_scope", scope, expire)
    }
    resp.AccessToken = token.AccessToken
    resp.ExpireIn = token.ExpireIn
    resp.Uid = proto.Uint32(auth_uid)
    return nil
}

func TokenInfo(req *TokenInfoReq, resp *TokenInfoResp) error {
    o := orm.NewOrm()
    token := TokenStore{}
    err := o.QueryTable("token_store").Filter("access_token", *req.AccessToken).One(&token)
    if err != nil {
        return err
    }
    resp.Uid = token.Uid
    resp.Appkey = token.ClientId
    resp.Scope = token.Scope
    resp.CreateAt = token.CreateTime
    resp.ExpireIn = token.ExpireIn
    return nil
}

